/**
 *
 * 项目中内部工具类,
 * 封装项目中使用的各种通用函数
 *
 * @namespace commonUtil
 *
 */

/**
 * 所有关于路径和数据的相关操作
 * @namespace pathUtil
 * @memberOf commonUtil
 */

/**
 * throw Error to stop executing and return some useful information
 * from angularJS
 * @memberOf commonUtil
 * @example
 * var moduleErr=minErr("moduleName")
 * moduleErr("some thing err, may it be {1} or be {2}",$info1,$info2)
 * @param  {string} module 模块名称
 * @return {function}
 */
function minErr(module) {
    return function() {
        var message = arguments[0],
            matches;
        matches = message.match(/\{\d+\}/g);
        if (undefined !== arguments[1])
            for (var i = 0; i < matches.length; i++) {
                message = message.replace(matches[i], arguments[parseInt(matches[i].slice(1, -1))]);
            }
        message = "module: " + module + " message: " + message;
        throw new Error(message);
    };
}

/**
 * 整个项目的入口,所有东西的入口
 *
 * @namespace jc
 *
 */
jc = window.jc = {
    /**
     * refer to {@link documentParse.render}
     * @function render
     * @memberOf jc
     */
    render: render,
    /**
     * refer to {@link commonUtil.minErr}
     * @function err
     * @memberOf jc
     */
    err: minErr,
    /**
     * refer to {@link commonUtil.getType}
     * @memberOf jc
     */
    getType: getType,
    /**
     * refer to {@link tempHelper.tempHelper}
     * @function th
     * @memberOf jc
     */
    th: tempHelper,
    /**
     * refer to {@link pubsub.initPB}
     * @function
     * @memberOf jc
     */
    initPB: initPB,
    /**
     * refer to {@link commonUtil.mix}
     * @memberOf jc
     */
    mix: mix,

    util:{
        isInDocument:isInDocument,
        nextUid:$nextUid
    }
};

/**
 * 用来做js继承
 * 代码中有具体注释，可参阅源码
 * 在测试中也有关于mix的应用
 * @example
 * var child ={child:childFun}
 * jc.mix(true,child,{p:parentFun})
 * //child 就变为  {child:childFun,p:parentFun}
 * @memberOf commonUtil
 * @param {boolean=} [isDeep=false] 是否是深度copy
 * @param {*} child
 * @param {*} parent
 *
 */
function mix() {
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false

    // 如果第一个参数为布尔,判定是否深拷贝
    if (typeof target === "boolean") {
        deep = target
        target = arguments[1] || {}
        i++
    }

    //确保接受方为一个复杂的数据类型
    if (typeof target !== "object" && getType(target) !== "function") {
        target = {}
    }

    //如果只有一个参数，那么新成员添加于mix所在的对象上
    if (i === length) {
        target = this
        i--
    }

    for (; i < length; i++) {
        //只处理非空参数
        if ((options = arguments[i]) != null) {
            for (name in options) {
                src = target[name]
                copy = options[name]

                // 防止环引用
                if (target === copy) {
                    continue;
                }
                if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {

                    if (copyIsArray) {
                        copyIsArray = false
                        clone = src && Array.isArray(src) ? src : []

                    } else {
                        clone = src && isPlainObject(src) ? src : {}
                    }

                    target[name] = jc.mix(deep, clone, copy)
                } else if (copy !== void 0) {
                    target[name] = copy
                }
            }
        }
    }
    return target
}

//配置
var config = jc.config = {
    tplRootUrl: './views/'
};
// function invokeLater() {
//     return function() {
//         invokeQueue[insertMethod || 'push']([provider, method, arguments]);
//         return moduleInstance;
//     };
// }

var jcError = minErr('jc');
//jquery引入 判定
if (!jQuery) {
    jcError("jc depends on jQuery");
}

var rword = /[^, ]+/g;

//类型判定
var class2type = {};
var serialize = Object.prototype.toString;
"Boolean Number String Function Array Date RegExp Object Error".replace(rword, function(name) {
    class2type["[object " + name + "]"] = name.toLowerCase()
});
/**
 * 获取具体类型,并返回小写的类型,支持
 * Boolean Number String Function Array Date RegExp Object Error
 * 的判定
 * @memberOf commonUtil
 * @example
 * var a={}
 * getType(a)//object
 * var reg = \abc\
 * getType(reg) //regexp
 *
 * @param  {*} unKnown
 * @return {String}
 */
function getType(obj) { //取得类型
    if (obj == null) {
        return String(obj)
    }
    // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准，可以将正则字面量当作函数使用，因此typeof在判定正则时会返回function
    return typeof obj === "object" || typeof obj === "function" ?
        class2type[serialize.call(obj)] || "object" :
        typeof obj
}



var noop = function() {};

var UniObj = {};

var documentFragment = document.createDocumentFragment();


/**
 * 将array转换成object类型
 * array值将变成object 的key
 * val将变成object 的value值
 *
 *
 * @memberOf commonUtil
 * @param  {array} array
 * @param  {*} val
 * @return {objectd}
 */
function oneObject(array, val) {
    if (typeof array === "string") {
        array = array.match(rword) || []
    }
    var result = {},
        value = val !== void 0 ? val : 1
    for (var i = 0, n = array.length; i < n; i++) {
        result[array[i]] = value
    }
    return result
}
/**
 *
 * 通过路径获取具体的值
 * @memberOf pathUtil
 * @example
 * var obj={
 *     a:{
 *         b:"aa",
 *         c:[1,2,3,4]
 *     }
 * }
 * dotPath(obj,"a.b")//"aa"
 * dotPath(obj,"a.c.2")// 3
 * @param  {object} object data
 * @param  {string} path
 * @return {*}
 */
function dotPath(object, path) {
    var re = object;
    path = path.split('.')
    for (var i = 0; i < path.length; i++) {
        if (re[path[i]] === undefined) {
            return;
        } else {
            re = re[path[i]]
        }
    }
    return re
}
/**
 *
 * not support array path
 * 根据索引的路径进行赋值
 * @example
 * var object={
 *     a:{
 *         b:3
 *     }
 * }
 * dotPathVal(object,"a.b",1)
 * //object    {a:{b:1}}
 * @memberOf pathUtil
 * @param  {object} object data
 * @param  {string} path
 * @param  {*} val    the value to be evaluated
 */
function dotPathVal(object, path, val) {
    var pathArr = path.match(/[^.]+/g),
        re = object,
        length = pathArr.length - 1;
    for (var i = 0; i < length; i++) {
        re = re[pathArr[i]]
    }
    re[pathArr[length]] = val
}

/**
 * @typedef {Object} pathResult
 * @property {object|array} obj 父引用
 * @property {*} value  具体值
 */
/**
 * 根据数组路径来返回值和其父引用
 * @memberOf pathUtil
 * @param  {object} object
 * @param  {array|string} path
 * @return {pathResult}
 */
function arrPathRef(object, path) {
    if (typeof path === 'string') {
        path = path.split('.')
    }
    var length = path.length - 1,
        re = object;
    try {
        for (var i = 0; i < length; i++) {
            re = re[path[i]]
        }
    } catch (e) {
        minErr("path Error")("the path:'{1}' can refer correct data {2}", path.join('.'), JSON.stringify(object))
    }
    return {
        obj: re,
        ref: path[length]
    }
}

function arrPathVal(object, arrPath, value) {
    var re = arrPathRef(object, arrPath)
    if (re.obj instanceof Array && typeof re.obj.set === 'function') {
        re.obj.set(re.ref, value)
    } else
        re.obj[re.ref] = value
}

/**
 * 遍历出最深层的数据路径
 * @memberOf pathUtil
 * @param  {object} o
 * @return {array}  path    路径集合
 */
function collectionPath(o, path) {
    var re = [],
        key,
        type;
    path = path || []
    type = getType(o)
    if (type === "object") {
        for (key in o) {
            if (o.hasOwnProperty(key)) {
                type = getType(o[key])
                path.push(key)
                //re.push(path.concat())
                if (type === "object" || type === "array") {
                    re = re.concat(collectionPath(o[key], path))
                } else {
                    re.push(path.concat())
                }
                path.splice(path.length - 1, 1)
            }
        }
    } else if (type === "array") {
        o.forEach(function(v, i, arr) {
            path.push(i)
            // re.push(path.concat())
            type = getType(v)
            if (type === "object" || type === "array") {
                re = re.concat(collectionPath(o[i], path))

            } else {
                re.push(path.concat())
            }
            path.splice(path.length - 1, 1)

        })
    }
    return re;
}

/**
 * 判定是否在指定document下
 * @param  {node=}  parent 默认为document.body
 * @param  {node}  child
 * @return {Boolean}
 */
function isInDocument(parent, child) {
    if (arguments.length === 1) {
        child = parent
        parent = document.body
    }
    if (child) {
        while (parent!==child) {
            child=child.parentNode
            if (!child ) {
                return false
            }
        }
        return true
    }
    return false
}

function isPlainObject(obj) {
    return !!obj && typeof obj === "object" && Object.getPrototypeOf(obj) === Object.prototype
}

//unique Id generator

var $$uid = ["0", "0", "0"];
/**
 * 生成本框架作用域内的唯一id字符串
 * @memberOf commonUtil
 * @return {string}
 */
function $nextUid() {
    var index = $$uid.length;
    var digit;
    while (index) {
        index--;
        digit = $$uid[index].charCodeAt(0);
        if (digit == 57 /*'9'*/ ) {
            $$uid[index] = 'A';
            return $$uid.join('');
        }
        if (digit == 90 /*'Z'*/ ) {
            $$uid[index] = '0';
        } else {
            $$uid[index] = String.fromCharCode(digit + 1);
            return $$uid.join('');
        }
    }
    $$uid.unshift('0');
    return $$uid.join('');
}



/**
 *
 * get parent path, the path format likes this "a.b.e.1"，
 * and we can get "a.b.e"
 * so we only need to remove the last dot
 * @memberOf pathUtil
 * @param  {String} strPath
 * @return {String}
 */
function getParentPath(strPath) {
    return strPath.replace(/(.*)(\..*?)$/, "$1")
}
// function getAttributeNodes(elem) {
//     var re = {};
//     for (var i = 0, len = elem.attributes.length; i < len; i++) {
//         var name = elem.attributes[i].nodeName;
//         var value = elem.attributes[i].nodeValue;
//         if (elem.attributes[i].specified)
//             re[name] = value
//     }
//     return re
// }